package me.itsoo.artemis.framework.repository.interceptor;

import lombok.Getter;
import me.itsoo.artemis.framework.core.util.Assert;
import me.itsoo.artemis.framework.core.util.CollectionUtils;

import java.util.Collection;
import java.util.Objects;

/**
 * SqlInterceptorChain
 *
 * @author zxy
 * @version 0.0.1
 * @see me.itsoo.artemis.framework.repository.mapper.SqlMapperProxy
 * @since 2023/5/23
 */
public class SqlInterceptorChain {

    private final InnerWrapper root;

    private final ThreadLocal<InnerWrapper> tl = ThreadLocal.withInitial(this::getRoot);

    public SqlInterceptorChain(Collection<SqlInterceptor> sqlInterceptors) {
        InterceptorOrderSorts.sort(sqlInterceptors);

        this.root = InnerWrapper.create(CollectionUtils.findFirst(sqlInterceptors));

        if (Objects.nonNull(root)) {
            initInterceptorChains(sqlInterceptors);
        }
    }

    /**
     * 初始化拦截器链
     *
     * @param sqlInterceptors List
     */
    private void initInterceptorChains(Collection<SqlInterceptor> sqlInterceptors) {
        InnerWrapper curr = root;

        final SqlInterceptor[] siArray = sqlInterceptors.toArray(new SqlInterceptor[0]);

        for (int i = 1, len = siArray.length; i < len; i++) {
            final InnerWrapper temp = InnerWrapper.create(siArray[i]);
            if (Objects.nonNull(temp)) {
                curr.setNext(temp);
                curr = temp;
            }
        }
    }

    /**
     * 获取根节点
     *
     * @return InnerWrapper
     */
    private InnerWrapper getRoot() {
        return this.root;
    }

    /**
     * 获取当前节点
     *
     * @return SqlInterceptor
     */
    public SqlInterceptor getCurr() {
        final InnerWrapper curr = tl.get();
        if (Objects.isNull(curr)) {
            tl.remove();
            return null;
        }

        tl.set(curr.getNext());
        return curr.getSqlInterceptor();
    }

    /**
     * InnerWrapper
     */
    @Getter
    private static class InnerWrapper {

        private final SqlInterceptor sqlInterceptor;

        private InnerWrapper next;

        private InnerWrapper(SqlInterceptor sqlInterceptor) {
            Assert.notNull(sqlInterceptor, "'sqlInterceptor' cannot be null");
            this.sqlInterceptor = sqlInterceptor;
        }

        static InnerWrapper create(SqlInterceptor sqlInterceptor) {
            return Objects.nonNull(sqlInterceptor) ? new InnerWrapper(sqlInterceptor) : null;
        }

        void setNext(InnerWrapper innerWrapper) {
            this.next = innerWrapper;
        }
    }
}
